Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test case and proposed fix for path component with trailing colon (and string) #708

Merged
merged 2 commits into from
Aug 1, 2018

Conversation

jfhamlin
Copy link
Contributor

If a pattern has no verb, match with the verb argument appended to last component.

As I understand the implementation, parsing out a "verb" from the input path is a convenience for matching. Whether the colon and string are actually a verb depends on the matching HTTP rule. This change gives a verb-less pattern a chance to match a path by assuming the colon+string suffix are a part of the final component.

There is a backwards compatibility issue here: this can change the behavior of existing rule sets. For example:

    option(google.api.http) = {
      get: "/users/{user_id}"
    };
    option(google.api.http) = {
      get: "/users/{user_id}:averb"
    };

Before this change, only the second rule matched a request for /users/123:averb. After this change, both rules match (correctly, by my understanding of https://github.com/googleapis/googleapis/blob/master/google/api/http.proto). Note that the spec describes a deterministic way of breaking the tie: // **NOTE:** All service configuration rules follow "last one wins" order. grpc-ecosystem/grpc-gateway's implementation looks buggy on this score, since it accepts the first matching handler.

Fixes #224

See grpc-ecosystem#224

Signed-off-by: James Hamlin <james@goforward.com>
@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed (or fixed any issues), please reply here (e.g. I signed it!) and we'll verify it.


What to do if you already signed the CLA

Individual signers
Corporate signers

@codecov-io
Copy link

codecov-io commented Jul 24, 2018

Codecov Report

Merging #708 into master will increase coverage by <.01%.
The diff coverage is 62.5%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #708      +/-   ##
==========================================
+ Coverage   56.43%   56.43%   +<.01%     
==========================================
  Files          30       30              
  Lines        3046     3053       +7     
==========================================
+ Hits         1719     1723       +4     
- Misses       1158     1159       +1     
- Partials      169      171       +2
Impacted Files Coverage Δ
runtime/mux.go 44.16% <ø> (ø) ⬆️
runtime/pattern.go 89.79% <62.5%> (-1.64%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 209d7ce...6d5b579. Read the comment docs.

@jfhamlin
Copy link
Contributor Author

fyi @yugui @tmc

After this change, both rules match (correctly, by my understanding of https://github.com/googleapis/googleapis/blob/master/google/api/http.proto).

I'll provide a little more color on why I think this is so. Here's the relevant section of http.proto:

// The syntax of the path template is as follows:
//
//     Template = "/" Segments [ Verb ] ;
//     Segments = Segment { "/" Segment } ;
//     Segment  = "*" | "**" | LITERAL | Variable ;
//     Variable = "{" FieldPath [ "=" Segments ] "}" ;
//     FieldPath = IDENT { "." IDENT } ;
//     Verb     = ":" LITERAL ;

This is the syntax of templates, not the paths templates are to match. It's actually a template that defines the syntax of the input paths it matches. If a template doesn't include a verb, then any trailing colon and string in an input path must be a part of the final segment.

@googlebot
Copy link

CLAs look good, thanks!

…onent

Parsing out a "verb" from the input path is a convenience for
parsing. Whether the colon and string are _actually_ a verb depends on
the matching HTTP rule. This change gives a verb-less pattern a chance
to match a path by assuming the colon+string suffix are a part of the
final component.

Fixes grpc-ecosystem#224

Signed-off-by: James Hamlin <james@goforward.com>
@jfhamlin jfhamlin changed the title Add test case and proposed fix for path component with trailing colon Add test case and proposed fix for path component with trailing colon (and string) Jul 24, 2018
@jfhamlin
Copy link
Contributor Author

bump on this (and cc @johanbrandhorst since i've seen you chime in on recent PRs 😃)

},
},
reqMethod: "GET",
reqPath: "/foo/bar:123",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the verb in this case "123"? What does it mean to have a verb of 123?

Copy link
Contributor Author

@jfhamlin jfhamlin Jul 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the verb in this case "123"?

My reading of the HttpRule spec makes me interpret it like this: this path matches the path template in the stub pattern, which has no verb, so "123" isn't a verb (I try to make a case for this reading in #708 (comment)). Instead, "bar:123" is the captured value of the id variable.

To flesh this out further:

  • An HTTP rule path template describes a set of paths, basically by a regular expression
  • If a request path is matched by an RPC's HTTP rule (regular expression), then it is handled by that RPC, with fields mapped according to the rule
  • It follows that if an HTTP rule path template doesn't end with a verb, then it expects no verb, and the trailing path component will be scanned the same way as any other segment or variable

Here's another way to think about it: an alternative implementation of these patterns in grpc-gateway could have been to build up golang regexp.Regexps directly from the HTTP rules. Then when a request comes in, iterate through the regexps and use the last one that matches the path. Example mappings from rule to regular expression ([^/]* probably isn't precisely correct, it's just an approximation of "The syntax * matches a single path segment"):

get: "/foo/{id}" => "/foo/(?P<id>[^/]*)"
get: "/foo/{id}:myverb" => "/foo/(?P<id>[^/]*):myverb"
get: "/foo/{id}/bar" => "/foo/(?P<id>[^/]*)/bar"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@achew22, does this make sense?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. You are trying to describe the positive case, and not the negative case as I had interpreted it. That seems entirely reasonable. Merging.

Thanks so much for fighting through my confusion and for contributing to the project as a whole!

@achew22 achew22 merged commit 7226a0d into grpc-ecosystem:master Aug 1, 2018
@jfhamlin jfhamlin deleted the fix/colon-path-segment branch August 1, 2018 23:59
dmacthedestroyer pushed a commit to dmacthedestroyer/grpc-gateway that referenced this pull request Aug 2, 2018
* Add explicit dependency versions (grpc-ecosystem#696)

Version constraints are copied from existing Bazel rules. In the
future, these version upgrades must be performed atomically.

* Add OpenTracing support to docs (grpc-ecosystem#705)

* protoc-gen-swagger: support all well-known wrapper types

There were a few well-known wrapper types missing from
the wkSchemas map. In specific UInt32Value, UInt64Value
and BytesValue. This change handles them (and maps them to
the same swagger types as the non-wrapped values)

This also fixes the mapping of Int64Value. The Int64Value handling
maps the value to a swagger integer. The documentation for
Int64Value indicates that it should be mapped to a JSON
string (also the mapping for normal int64 in protoc-gen-swagger
maps it to a string, so this was inconsistent.)

* Add test case and proposed fix for path component with trailing colon (and string) (grpc-ecosystem#708)

* If a pattern has no verb, match with a verb arg appended to last component

Parsing out a "verb" from the input path is a convenience for
parsing. Whether the colon and string are _actually_ a verb depends on
the matching HTTP rule. This change gives a verb-less pattern a chance
to match a path by assuming the colon+string suffix are a part of the
final component.

Fixes grpc-ecosystem#224

Signed-off-by: James Hamlin <james@goforward.com>

* Fix up examples

* Make support paths option

Make protoc-gen-grpc-gateway support paths option such like golang/protobuf#515.
@kassiansun
Copy link

Then, how to gain the old behavior of {id}:verb? Breaking the old behavior without any solution?

@kassiansun
Copy link

ping @jfhamlin @achew22

@kassiansun
Copy link

Who can tell me what's the meaning of syntax {id}:verb after this patch, if it matches the whole segment to id?

@kassiansun
Copy link

Actually it matches to the {id} route, which has no trailing :verb.

And in #224 , I don't think URN is valid inside URL, they are exclusive.

@jfhamlin
Copy link
Contributor Author

Who can tell me what's the meaning of syntax {id}:verb after this patch, if it matches the whole segment to id?

Hi @kassiansun. It would be useful in these examples to not only have the path pattern but also some example paths and expected variable binding. For example:

pattern: /{id}:verb
path: /foo:verb match binding: id = foo
path: /foo no match

pattern: /{id}
path: /foo:verb match binding: id = foo:verb
path: /foo match binding: id = foo

Does that make sense?

@kassiansun
Copy link

No, the real behavior is /{id}:verb matches /foo:verb with id=foo:verb. Have you tested this patch?

@kassiansun
Copy link

#224 makes no sense, users should not use : in url as grpc-gateway use : for verb literals. If they really want to use, use url queries, don't mess with path variable.

johanbrandhorst added a commit that referenced this pull request Oct 1, 2018
The solution for #224 turned out to break backwards
compatibility, so we're going to have to find another
solution for users who desire this behaviour.
Also adds test cases from #760.
achew22 pushed a commit that referenced this pull request Oct 1, 2018
The solution for #224 turned out to break backwards
compatibility, so we're going to have to find another
solution for users who desire this behaviour.
Also adds test cases from #760.
jfhamlin added a commit to jfhamlin/grpc-gateway that referenced this pull request Jun 15, 2019
adasari pushed a commit to adasari/grpc-gateway that referenced this pull request Apr 9, 2020
… (and string) (grpc-ecosystem#708)

* If a pattern has no verb, match with a verb arg appended to last component

Parsing out a "verb" from the input path is a convenience for
parsing. Whether the colon and string are _actually_ a verb depends on
the matching HTTP rule. This change gives a verb-less pattern a chance
to match a path by assuming the colon+string suffix are a part of the
final component.

Fixes grpc-ecosystem#224

Signed-off-by: James Hamlin <james@goforward.com>
adasari pushed a commit to adasari/grpc-gateway that referenced this pull request Apr 9, 2020
The solution for grpc-ecosystem#224 turned out to break backwards
compatibility, so we're going to have to find another
solution for users who desire this behaviour.
Also adds test cases from grpc-ecosystem#760.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

404s using colons in the middle of the last path segment
5 participants